import * as Cesium from 'cesium'
import { accessToken, initConfig } from '@config/cesiumConf'


Cesium.Ion.defaultAccessToken = accessToken;

const terrainProvider = new Cesium.CesiumTerrainProvider({
    url: ''
})

export function initCesium(): Promise<Cesium.Viewer> {
    let viewer: Cesium.Viewer
    viewer = new Cesium.Viewer('cesiumViewer', { ...initConfig })
    //调整阴影明暗
    viewer.shadowMap.darkness = 0.1; 
    //模拟太阳光照
    viewer.scene.globe.enableLighting = true
    // 显示帧速(FPS)
    viewer.scene.debugShowFramesPerSecond = true
    //关闭大气层显示
    viewer.scene.skyAtmosphere.show = true
    //突出显示地形填充图块的颜色
    // viewer.scene.globe.fillHighlightColor = Color.RED
    //控制视角不转到地形下
    viewer.scene.globe.depthTestAgainstTerrain = true;
    // 隐藏太阳和月亮
    viewer.scene.sun.show = false
    viewer.scene.moon.show = false
    //解决锯齿
    viewer.scene.postProcessStages.fxaa.enabled = true

    // 关闭天空盒，否则会显示天空颜色
    viewer.scene.skyBox.show = false
    // 访问球体对象（viewer.scene.globe） 并设置颜色
    viewer.imageryLayers.remove(viewer.imageryLayers.get(0))
    // viewer.scene.globe.baseColor = Cesium.Color.fromCssColorString("#18172B")
    viewer.scene.globe.baseColor = Cesium.Color.fromCssColorString("#18172B")
    // viewer.scene.globe.baseColor = Cesium.Color.WHITE.withAlpha(0)
    // preventCameraUnderGround(viewer)
    // fixCameraArea({viewer})
    // enableCameraZoomDistance(viewer)
    viewer.scene.skyBox = new Cesium.SkyBox({
		sources: {
			positiveX: '/skyBox/right.jpg',
			negativeX: '/skyBox/left.jpg',
			positiveZ: '/skyBox/up.jpg',
			negativeZ: '/skyBox/down.jpg',
			positiveY: '/skyBox/front.jpg',
			negativeY: '/skyBox/back.jpg' 
		}
	})
    return Promise.resolve(viewer)
}

// 通过世界坐标获取获取具有高程的世界坐标
export async function getTerrainHeight(cartesians: Array<Cesium.Cartesian3>, deltaHeight?:number):Promise<Array<Cesium.Cartesian3>> {
    deltaHeight = deltaHeight ?? 0
    // 世界坐标转地理坐标
    let temp:Array<Cesium.Cartographic> = []
    let ret:Array<Cesium.Cartesian3> = []
    cartesians.forEach(cartesian => {
        temp.push(Cesium.Cartographic.fromCartesian(cartesian))
    })
    const promise = Cesium.sampleTerrainMostDetailed(terrainProvider, temp)
    const positons = await Promise.resolve(promise)
    positons.forEach(({longitude, latitude, height}) => {
        ret.push(Cesium.Cartographic.toCartesian(new Cesium.Cartographic(longitude, latitude, height + deltaHeight!)))
    })
    return Promise.resolve(ret)
}

//调整3dtiles贴地高度
export function adjustHeight(tileset: Cesium.Cesium3DTileset, height: number = 0) {
    const cartographic = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center)
    const surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0)
    const offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, height)
    const translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3())
    tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation)
}


//获取两个点之间的距离（包含水平距离horizontal，垂直距离vertical，直线距离straight）
type Distance = {
    horizontal: number, 
    vertical: number, 
    straight: number
}
export function  getDistance(start:Cesium.Cartesian3, end: Cesium.Cartesian3): Distance {
	//世界坐标转换为地理坐标
	const startCartographic = Cesium.Cartographic.fromCartesian(start)
	const endCartographic = Cesium.Cartographic.fromCartesian(end)
	//初始化测地线
	const geodesic = new Cesium.EllipsoidGeodesic()
	//设置测地线起点和终点
	geodesic.setEndPoints(startCartographic, endCartographic)
	//计算出水平距离
	const horizontal = geodesic.surfaceDistance
	//计算出垂直距离
	const vertical = Math.abs(endCartographic.height - startCartographic.height)
	//计算出直线距离
	const straight = Math.sqrt(horizontal**2 + vertical**2)
	//返回表面距离
	return { horizontal, vertical, straight }
}

// 批量加载3D tiles
type PromiseStatus<T> = {
    status: 'fulfilled' | 'rejected',
    value?: T,
    reason?: any
}
export function asyncLoad3DTiles(url: string, name: string, height: number):Promise<Cesium.Cesium3DTileset>{
    return new Promise((resolve, reject) => {
        const tileset = new Cesium.Cesium3DTileset({ url,
            skipLevelOfDetail: true,
            preferLeaves: true,
            maximumMemoryUsage: 1024,
            // immediatelyLoadDesiredLevelOfDetail: true
        })
        tileset.readyPromise.then(tileset => {
            adjustHeight(tileset, height)
            resolve(tileset)
        }).catch(error => {
            reject(error)
        })
    })
}
export async function batchLoad3Dtiles(paths: Array<string> = [], height: number = 0)
    : Promise<Array<PromiseStatus<Cesium.Cesium3DTileset>>> {
    let promises: Array<Promise<any>> = []
    paths.forEach(url => {
        promises.push(asyncLoad3DTiles(url, '', height))
    })
    let results: Array<PromiseStatus<Cesium.Cesium3DTileset>>
    results = await Promise.allSettled(promises)
    return Promise.resolve(results)
}
// 加载geojson
export async function loadGeoJson(path: string): Promise<Cesium.GeoJsonDataSource> {
    let dataSource: Cesium.GeoJsonDataSource
    try {
        dataSource = await Cesium.GeoJsonDataSource.load(path)
    } catch {
        dataSource = new Cesium.GeoJsonDataSource()
    }
    return dataSource
}
//防止相机视角翻转到地底下
export function preventCameraUnderGround(viewer:Cesium.Viewer, pitchAngle:number = -10){
   viewer.scene.preRender.addEventListener(()=> {
        let eye = viewer.camera.positionCartographic;
        // 判断相机坐标是否小于阈值，若小于阈值，则保持视点方位，修改相机高度
        const pitch = Cesium.Math.toDegrees(viewer.camera.pitch);
        if(pitch > pitchAngle){
            viewer.camera.setView({
                destination: Cesium.Cartesian3.fromRadians(eye.longitude, eye.latitude, eye.height),
                orientation: {
                    heading:viewer.camera.heading,
                    pitch: Cesium.Math.toRadians(pitchAngle),
                    roll: viewer.camera.roll
                }
            })
        }
    })
}

//限制相机翻转到地下并且限制移动区域
type FixCameraArea = {
    viewer:Cesium.Viewer
    rectangle?: Cesium.Rectangle
    pitchAngle?:number
}
export function fixCameraArea(options:FixCameraArea) {
    let { viewer, rectangle, pitchAngle } = options
    viewer.scene.preRender.addEventListener(()=> {
        //防止翻转到地下
        let eye = viewer.camera.positionCartographic;
        // 判断相机坐标是否小于阈值，若小于阈值，则保持视点方位，修改相机高度
        let pitch = Cesium.Math.toDegrees(viewer.camera.pitch);
        pitchAngle = pitchAngle ?? 10
        if(pitch > pitchAngle){
            viewer.camera.setView({
                destination: Cesium.Cartesian3.fromRadians(eye.longitude, eye.latitude, eye.height),
                orientation: {
                    heading:viewer.camera.heading,
                    pitch: Cesium.Math.toRadians(pitchAngle),
                    roll: viewer.camera.roll
                }
            })
        }
    })
}

//根据得到的世界坐标生成矩形
export function getRectangle(position:Cesium.Cartesian3, delta?:[number, number]):Array<Cesium.Cartesian3> {
    let [deltaLon, deltaLat] = [0.001, 0.001]
    if (delta) { [deltaLon, deltaLat] = delta }
    let {longitude, latitude, height} = Cesium.Cartographic.fromCartesian(position)
    let east = new Cesium.Cartographic(longitude + deltaLon, latitude + deltaLat, height)
    let south = new Cesium.Cartographic(longitude + deltaLon, latitude - deltaLat, height)
    let west = new Cesium.Cartographic(longitude - deltaLon, latitude - deltaLat, height)
    let north = new Cesium.Cartographic(longitude - deltaLon, latitude + deltaLat, height)
    return [
        Cesium.Cartographic.toCartesian(east),
        Cesium.Cartographic.toCartesian(south),
        Cesium.Cartographic.toCartesian(west),
        Cesium.Cartographic.toCartesian(north)
    ]
}

//限制镜头高度
export function enableCameraZoomDistance(viewer:Cesium.Viewer) {
    viewer.scene.screenSpaceCameraController.maximumZoomDistance = 1500
    viewer.scene.screenSpaceCameraController.minimumZoomDistance = 10
}

export function getCanvasRect(text:string):HTMLCanvasElement {
    const canvas = document.createElement("canvas")
    canvas.width = 300
    canvas.height = 40
    const ctx = canvas.getContext('2d')
    if (ctx) {
        ctx.clearRect(0, 0, canvas.width, canvas.height)
        ctx.font = '30px Calibri'
        ctx.fillStyle = "#fff"
        ctx.fillText(text, 0, 35)
    }
    return canvas
}